/*
 * Copyright 2002-2019 Intel Corporation.
 * 
 * This software is provided to you as Sample Source Code as defined in the accompanying
 * End User License Agreement for the Intel(R) Software Development Products ("Agreement")
 * section 1.L.
 * 
 * This software and the related documents are provided as is, with no express or implied
 * warranties, other than those that are expressly stated in the License.
 */

/*
 * A test which includes a large number of different push instructions.
 *
 * The idea is that we validate their behaviour here, so that we can check 
 * that they're still working correctly when we rewrite their addressing
 * with Pin.
 *
 * Code assumes Gcc inline asm, so won't run on Windows.
 * This shouldn't matter, since none of the rewriting is OS dependent.
 */

#include <stdio.h>
extern void * pushIW_(void *stack);

typedef long int addrint;

/* Macros for building test routines. */
#define switchStack(newSP) \
    __asm__ ("mov  %0,%%esp": :"r"(newSP): "%esp")

#define readStack(SP) \
    __asm__ ("mov  %%esp,%0": "=r"(SP) :: "%esp")

static void * pushI(void *stack)
{
    // Don't use anything on the stack, since we're about to switch esp.
    // (Since they're normally addressed relative to ebp locals should be OK,
    // but this is safest)
    register void * sp  asm("%edx");
    register void * osp asm("%ecx");

    readStack(sp);
    switchStack (stack);
    __asm__ ("pushl $99");
    readStack(osp);
    switchStack(sp);
    
    return osp;
}

static void * pushIW(void *stack)
{
    // Don't use anything on the stack, since we're about to switch esp.
    // (Since they're normally addressed relative to ebp locals should be OK,
    // but this is safest)
    register void * sp  asm("%edx");
    register void * osp asm("%ecx");

    readStack(sp);
    switchStack (stack);
    __asm__ ("pushw $-5");
    readStack(osp);
    switchStack(sp);
    
    return osp;
}

static void * pushSP(void *stack)
{
    register void * sp  asm("%edx");
    register void * osp asm("%ecx");

    readStack(sp);
    switchStack (stack);
    __asm__ ("pushl %esp");
    readStack(osp);
    switchStack(sp);
    
    return osp;
}

static void * pushSPIndirect(void *stack)
{
    register void * sp  asm("%edx");
    register void * osp asm("%ecx");

    readStack(sp);
    switchStack (stack);
    __asm__ ("pushl (%esp)");
    readStack(osp);
    switchStack(sp);
    
    return osp;
}

/* Can't easily check the results for this one, but at least we can 
 * see that the correct number of things were pushed.
 */
static void * pushA(void *stack)
{
    register void * sp  asm("%edx");
    register void * osp asm("%ecx");

    readStack(sp);
    switchStack (stack);
    __asm__ ("pusha");
    readStack(osp);
    switchStack(sp);
    
    return osp;
}

/* Can't easily check the results for this one, but at least we can 
 * see that the correct number of things were pushed.
 */
static void * pushF(void *stack)
{
    register void * sp  asm("%edx");
    register void * osp asm("%ecx");

    readStack(sp);
    switchStack (stack);
    __asm__ ("pushf");
    readStack(osp);
    switchStack(sp);
    
    return osp;
}

static unsigned char xlat(unsigned char * base, unsigned char index)
{
    // ebx is the PIC register, but its use is fixed in xlat. Preserve original value.

    register unsigned char result asm ("%al") = index;

    __asm__ (
             "movl %%ebx, %%edx;"
             "movl %2, %%ebx;"
             "xlat;"
             "movl %%edx, %%ebx;"
             : "=a"(result)
             : "0"(result), "m"(base)
             : "%edx");

    return result;
}

static int xlatTest()
{
    unsigned char notV[256];
    unsigned char xlated[256];
    int i;
    int failures = 0;
    for (i=0;i<256; i++)
    {
        notV[i] = (unsigned char)~i;
    }

    for (i=0;i<256;i++)
    {
        xlated[i] = xlat(notV, (unsigned char)i);
    }

    for (i=0; i<256; i++)
    {
        if (xlated[i] != notV[i])
        {
            printf("XLAT failed : got %02x expected %02x\n", xlated[i], notV[i]);
            failures++;
        }
    }

    printf ("XLAT test %s\n", failures ? "FAILED" : "Passed");
    return failures;
}

struct result
{
    void *  before;
    void *  after;
    addrint expected;
    addrint seen;
};

int printResult (const char * test, struct result *r)
{
    printf ("%-8s %p %p  %4ld   %010p %010p %7ld\n",
            test, r->before, r->after, 
            (int)(((char *)r->after) - (char *)r->before),
            (void *) r->expected, (void *)r->seen, r->seen - r->expected);

    return (r->expected != r->seen);
}

int main (int argc, char ** argv)
{
    struct result r;
    addrint stack[32];
    addrint * stackp = &stack[32];
    int failures = xlatTest();

    printf ("              Stack Pointer                   Value\n");
    printf ("Test     Before     After      Delta   Expect     See          Delta\n");

    r.before = stackp;
    r.after  = pushI(stackp);
    r.expected = 99;
    r.seen   = stack[31];
    failures += printResult ("I",&r);

    r.before = stackp;
    r.after  = pushIW_(stackp); // Since ICC 11 is not supporting pushw in it's inline asm, the pushIW changed to an asm version pushIW_
    r.expected = -5;
    r.seen   = *(short *)(((char *)stackp)-2);
    failures += printResult ("IW",&r);

    r.before = stackp;
    r.after  = pushSP(stackp);
    r.expected = (addrint)stackp;                   /* push esp is an interesting case. See esp before decrement. */
    r.seen     = stack[31];
    failures += printResult ("%esp",&r);

    stack[31] = 101;
    r.before   = stackp-1;
    r.after  = pushSPIndirect(r.before);
    r.expected = 101;
    r.seen   = stack[30];
    failures += printResult("(%esp)", &r);
    
    r.before = stackp;
    r.after  = pushA(stackp);
    r.expected = 0;
    r.seen     = 0;
    failures += printResult ("pusha",&r);

    printf ("Flags result varies, not counted as a failure\n");
    r.before = stackp;
    r.after  = pushF(stackp);
    r.expected = 0x286;
    r.seen     = stack[31];
    printResult ("pushf",&r);

    printf ("Done\n");

    return failures;
}

